/* ECE6100 - Branch Predictor Project
 * 
 * Name: Michael Giardino
 * GTID: 901343599
 * email:mikelj@gatech.edu
 *
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include </usr/include/math.h>

enum PRED_TYPE {
	GAG = 0,
	GAP,
	PAG,
	GSHARE
};

enum COUNT {
	SNT = 0,
	WNT,
	WT,
	ST
};

struct PHT {
	int8_t hist_index;
	int32_t address;
	enum COUNT counter;
};

struct PAPHT {
    
};

struct predhist {
	int correct;
	int total;
} pred;

FILE *f1;

int error(char*);
int getbranch(int*, int*, int*);
int gobranch(enum COUNT);
int16_t mask(int16_t, int);
void sat_count2(enum COUNT*, int);
int addtag(int, int);

void GAg(int, int);
void GAp(int, int, int);/*
void PAg(int k_hist, int PHT_size);
void Gshare(int k_hist); */


int main (int argc, char *argv[]) {
	if (!(f1 = fopen("history.txt", "r"))) {
		error("No history.txt found...");
    }
	int i = 1;
	int k;
	int PHT_entries;
        
	enum PRED_TYPE pred_type;
	
    while (i < argc) {
        if (!(strcmp(argv[i], "-t"))) {
			if (!(strcmp(argv[i+1], "gag"))) {
				pred_type = GAG; 
			} else if (!(strcmp(argv[i+1], "gap"))) {
				pred_type = GAP;
			} else if (!(strcmp(argv[i+1], "pag"))) {
				pred_type = PAG;
			} else if (!(strcmp(argv[i+1], "gshare"))) {
				pred_type = GSHARE;
			} else {
				error("in argv[i] == valid branch prediction type...");
            }
			i++;
		}

		if (!(strcmp(argv[i], "-k"))) {
			k = atoi(argv[i+1]);
			i++;
		}

		if (!(strcmp(argv[i], "-s"))) {
			PHT_entries = atoi(argv[i+1]);
			i++;
		}
		i++;
	}
	printf("end of cmdline parse. k = %d, s = %d, t = %d\n", k, PHT_entries, pred_type);
	pred.correct = 0;
	pred.total = 0;
	
	if (pred_type == GAG) {
        if (k > 8) {
            error("For GAg, k <= 8\n");
        }
    	PHT_entries = pow(2, k);
		GAg(k, PHT_entries);
        
	} else if (pred_type = GAP) {
        if (k > 4) {
            printf("For GAp, k <= 4\n");
            return 1;
        }
        int PHT_width = pow(2, k);
        if (PHT_entries > 128) {
            error("For GAp, PHT size <= 128\n");
        }
        GAp(k, PHT_entries, PHT_width);
	} else if (pred_type = PAG) {
        PHT_entries = pow(2, k);
		PAg(k, PHT_entries, PHT_width);
	} else if (pred_type = GSHARE) {
		Gshare(k); 
	  else { 
		error("What the hell? Wrong prediction?\n");
	}
}

void GAg(int k_hist, int PHT_size) {
	printf("in GAg\n");
    
    printf("k = %d, PHT = %d\n", k_hist, PHT_size);

    int16_t GBHR = 0;
	struct PHT GPHT[PHT_size];
	int add;
	int target;
	int taken;
    int tot_taken = 0;
    int tot_nott = 0;
	
    
    
	int i = 0;
	for (i = 0; i < PHT_size; i++) {
		GPHT[i].hist_index = i;
		GPHT[i].address = 0;
		GPHT[i].counter = WNT;
	}
	
	while (getbranch(&add, &target, &taken)) {
		if (gobranch(GPHT[GBHR].counter)) {
			if (taken == 1) {
				pred.correct++;
			}
		} else {
			if (taken == 0) {
				pred.correct++;
			}
		}
		
        pred.total++;
		sat_count2(&GPHT[GBHR].counter, taken);
		
		GBHR << 1;
		if (taken == 1) {
			GBHR++;
            tot_taken++;
        } else {
            tot_nott++;
        }
		
		GBHR = mask(GBHR, k_hist);
	}
printf("total taken: %d, total not-taken: %d\n", tot_taken, tot_nott);   
printf("correct: %d, total: %d\n", pred.correct, pred.total);

}

void GAp(int k_hist, int PHT_entries, int PHT_size) {
	printf("in GAp\n");
    
    printf("k = %d, PHT = %d PPHTs = %d\n", k_hist, PHT_size, PHT_entries);

    int8_t GBHR = 0;
	struct PHT PPHT[PHT_entries][PHT_size];
	int add;
	int target;
	int taken;
    int tot_taken = 0;
    int tot_nott = 0;
    int tagsize = (int)log2((float)PHT_entries);
    int tag = 0;
    int i, j;
   
    // Initialize PPHT
    for (j = 0; j < PHT_entries; j++) {
        for (i = 0; i < PHT_size; i++) {
            PPHT[j][i].hist_index = i;
            PPHT[j][i].address = 0;
            PPHT[j][i].counter = WNT;
	    }
    }
    while (getbranch(&add, &target, &taken)) {
        tag = addtag(add, tagsize);
        if (gobranch(PPHT[tag][GBHR].counter)) {
			if (taken == 1) {
				pred.correct++;
			}
		} else {
			if (taken == 0) {
				pred.correct++;
			}
		}
		
        pred.total++;
		sat_count2(&PPHT[tag][GBHR].counter, taken);
		
		GBHR << 1;
		if (taken == 1) {
			GBHR++;
            tot_taken++;
        } else {
            tot_nott++;
        }
		
		GBHR = mask(GBHR, k_hist);

    }
    printf("total taken: %d, total not-taken: %d\n", tot_taken, tot_nott);   
    printf("correct: %d, total: %d\n", pred.correct, pred.total);
}   
    
void PAg(int k_hist, int PHT_entries, int PHT_size) {
    
    
    
    
}
int getbranch(int* add, int* target, int* taken) {
    char t[4];
	if (fscanf(f1, "%x %x %s", add, target, t) != EOF) {
        *taken = atoi(t);
        return 1;
	 } else {
	 	return 0;
	 }
}

int gobranch(enum COUNT counter) {
/*    printf("counter in gobranch: %d\n", counter); */
    
    if (counter <= 1)
		return 0;
	else
		return 1;
}

int addtag(int address, int k) {
    if (k == 5) {
        return address & 0x1F;
    } else if (k == 6) {
        return address & 0x3F;
    } else {
        return address & 0x7F;
    }
}

int16_t mask(int16_t GBHR, int k) {
	if (k == 2)
		return GBHR & 0x03;
	else if (k == 4)
		return GBHR & 0x0F;
	else if (k == 6)
		return GBHR & 0x3F;
	else
		return GBHR & 0xFF;
}

void sat_count2(enum COUNT *counter, int up) {
	if (up) {
		if (*counter == ST) {
			return;
		} else {
			(*counter)++;			
		}
	} else {
		if (*counter == SNT) {
			return;
		} else {
			(*counter)--;
		}
	}
	return;
}

int error(char* s)
{
	printf("%s\n Exiting unceremoniously...\n");
	return(1);
}
	
